#include <cmath>
#include <nice2d/objectfactory.h>
#include <nice2d/interpolator.h>

inline float easeInSine(float value)
{
    return 1 - std::cos((value * M_PI) / 2);
}

inline float easeOutSine(float value)
{
    return std::sin((value * M_PI) / 2);
}

inline float easeInOutSine(float value)
{
    return -(std::cos(M_PI * value) - 1) / 2;
}

inline float easeInQuad(float value)
{
    return value * value;
}

inline float easeOutQuad(float value)
{
    return 1 - (1 - value) * (1 - value);
}

inline float easeInOutQuad(float value)
{
    return value < 0.5 ? 2 * value * value : 1 - std::pow(-2 * value + 2, 2) / 2;
}

inline float easeInCubic(float value)
{
    return value * value * value;
}

inline float easeOutCubic(float value)
{
    return 1 - std::pow(1 - value, 3);
}

inline float easeInOutCubic(float value)
{
    return value < 0.5 ? 4 * value * value * value : 1 - std::pow(-2 * value + 2, 3) / 2;
}

inline float easeInQuart(float value)
{
    return value * value * value * value;
}

inline float easeOutQuart(float value)
{
    return 1 - std::pow(1 - value, 4);
}

inline float easeInOutQuart(float value)
{
    return value < 0.5 ? 8 * value * value * value * value : 1 - std::pow(-2 * value + 2, 4) / 2;
}

inline float easeInQuint(float value)
{
    return value * value * value * value * value;
}

inline float easeOutQuint(float value)
{
    return 1 - std::pow(1 - value, 5);
}

inline float easeInOutQuint(float value)
{
    return value < 0.5 ? 16 * value * value * value * value * value :
           1 - std::pow(-2 * value + 2, 5) / 2;
}

inline float easeInEvaluepo(float value)
{
    return value == 0 ? 0 : std::pow(2, 10 * value - 10);
}

inline float easeOutEvaluepo(float value)
{
    return value == 1 ? 1 : 1 - std::pow(2, -10 * value);
}

inline float easeInOutEvaluepo(float value)
{
    return value == 0
           ? 0
           : value == 1
           ? 1
           : value < 0.5 ? std::pow(2, 20 * value - 10) / 2
           : (2 - std::pow(2, -20 * value + 10)) / 2;
}

inline float easeInCirc(float value)
{
    return 1 - std::sqrt(1 - std::pow(value, 2));
}

inline float easeOutCirc(float value)
{
    return sqrt(1 - std::pow(value - 1, 2));
}

inline float easeInOutCirc(float value)
{
    return value < 0.5
           ? (1 - std::sqrt(1 - std::pow(2 * value, 2))) / 2
           : (std::sqrt(1 - std::pow(-2 * value + 2, 2)) + 1) / 2;
}

inline float easeInBack(float value)
{
    const float c1 = 1.70158;
    const float c3 = c1 + 1;
    return c3 * value * value * value - c1 * value * value;
}

inline float easeOutBack(float value)
{
    const float c1 = 1.70158;
    const float c3 = c1 + 1;
    return 1 + c3 * std::pow(value - 1, 3) + c1 * std::pow(value - 1, 2);
}

inline float easeInOutBack(float value)
{
    const float c1 = 1.70158;
    const float c2 = c1 * 1.525;

    return value < 0.5
           ? (std::pow(2 * value, 2) * ((c2 + 1) * 2 * value - c2)) / 2
           : (std::pow(2 * value - 2, 2) * ((c2 + 1) * (value * 2 - 2) + c2) + 2) / 2;
}

#define DEFINE_Interpolator(type,fn)\
    class A_B(Interpolator,type) : public Interpolator\
    {\
    public:\
        float interpolate(float value) override\
        {\
            return fn(value);\
        }\
    };\
    ObjectRegister<Interpolator,A_B(Interpolator,type),uint32_t> A_B(Interpolator,type)(type);

#define REGISTER_Interpolator(fn) DEFINE_Interpolator(A_B(InterpolatorType_,fn),fn)

REGISTER_Interpolator(easeInSine)
REGISTER_Interpolator(easeOutSine)
REGISTER_Interpolator(easeInOutSine)
REGISTER_Interpolator(easeInQuad)
REGISTER_Interpolator(easeOutQuad)
REGISTER_Interpolator(easeInOutQuad)
REGISTER_Interpolator(easeInCubic)
REGISTER_Interpolator(easeOutCubic)
REGISTER_Interpolator(easeInOutCubic)
REGISTER_Interpolator(easeInQuart)
REGISTER_Interpolator(easeOutQuart)
REGISTER_Interpolator(easeInOutQuart)
REGISTER_Interpolator(easeInQuint)
REGISTER_Interpolator(easeOutQuint)
REGISTER_Interpolator(easeInOutQuint)
REGISTER_Interpolator(easeInEvaluepo)
REGISTER_Interpolator(easeOutEvaluepo)
REGISTER_Interpolator(easeInOutEvaluepo)
REGISTER_Interpolator(easeInCirc)
REGISTER_Interpolator(easeOutCirc)
REGISTER_Interpolator(easeInOutCirc)
REGISTER_Interpolator(easeInBack)
REGISTER_Interpolator(easeOutBack)
REGISTER_Interpolator(easeInOutBack)

#undef DEFINE_Interpolator
#undef REGISTER_Interpolator

std::shared_ptr<Interpolator> Interpolator::create(InterpolatorType type)
{
    auto creator = ObjectFactory<Interpolator, uint32_t>::creator;
    if (creator == nullptr)
        return nullptr;

    auto interpolator = ObjectFactory<Interpolator, uint32_t>::create(type);
    return std::shared_ptr<Interpolator>(interpolator);
}

